package cn.dansj.controller;

import cn.dansj.common.utils.bean.CacheToken;
import cn.dansj.common.utils.enums.BooleanType;
import cn.dansj.entity.Menu;
import cn.dansj.entity.Role;
import cn.dansj.entity.RoleMenu;
import cn.dansj.entity.User;
import cn.dansj.service.*;
import cn.dansj.utils.transfer.PermissionUtils;
import cn.dansj.common.request.annotation.http.GetMethod;
import cn.dansj.common.request.annotation.http.PostMethod;
import cn.dansj.common.request.annotation.http.RequestMappingMethod;
import cn.dansj.common.request.annotation.http.RequestParam;
import cn.dansj.common.utils.json.DictMap;
import cn.dansj.enums.GlobalEnv;
import cn.dansj.common.utils.enums.ResponseCode;
import cn.dansj.exception.InterfaceResponseException;
import cn.dansj.common.request.RequestUtils;
import cn.dansj.common.redis.RedisUtils;
import cn.dansj.common.utils.transfer.*;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.TypeReference;
import cn.dansj.common.utils.bean.Return;
import cn.dansj.common.utils.crypt.Pbkdf2Sha256;
import cn.dansj.utils.project.ProjectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.data.domain.Page;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.sql.Timestamp;
import java.util.*;

@RequestMappingMethod("/user")
public class UserController {
    private static final Logger logger = LoggerFactory.getLogger(UserController.class);
    private final UserService userService;
    private final RoleService roleService;
    private final RoleMenuService roleMenuService;
    private final MenuService menuService;
    private final RedisUtils redisUtils;
    private final MinioUploadService minioUploadService;

    public UserController(UserService userService, RoleService roleService, RoleMenuService roleMenuService, MenuService menuService, RedisUtils redisUtils, MinioUploadService minioUploadService) {
        this.userService = userService;
        this.roleService = roleService;
        this.roleMenuService = roleMenuService;
        this.menuService = menuService;
        this.redisUtils = redisUtils;
        this.minioUploadService = minioUploadService;
    }

    @GetMethod(value = "/list/getData")
    public Return<?> userList(@RequestParam("search") JSONObject search, @RequestParam int limit, @RequestParam int page) {
        String username = search.getString("username");
        String mail = search.getString("mail");
        String role = search.getString("role");
        String status = search.getString("status");
        //获取查询条件及页码返回的用户列表
        Page<User> userPage = userService.search(page, limit, username, status, mail, role);
        List<User> content = userPage.getContent();
        content.forEach(e -> e.setPassword(""));
        return Return.T(content).setTotalRecord(userPage.getTotalElements());
    }

    @PostMethod(value = "/list/modify")
    public Return<?> modify(@RequestParam JSONObject request) {
        User user = JSON.parseObject(request.getJSONObject("user").toJSONString(), User.class);
        //加密密码
        boolean removeToken = false;
        if (Verification.checkNotNull(user.getPassword())) {
            user.setPassword(Pbkdf2Sha256.encode(user.getPassword()));
            removeToken = true;
        }
        String sequence = user.getSequence();

        String message;
        if (sequence != null && !Objects.equals(sequence, "")) {
            userService.updateUser(user);
            message = "信息更新成功";
        } else {
            //设置创建时的默认值
            Timestamp nowTime = GetTime.dbTimestamp();
            String userSequence;
            boolean reGet;
            do {
                //redis中获取sequence
                userSequence = ProjectUtils.getSequence("user").toString();
                User existUser = userService.getUserBySequence(userSequence);
                reGet = existUser != null;
            } while (reGet);
            user.setSequence(userSequence);
            user.setCrtTime(nowTime);
            user.setUpdateTime(nowTime);
            user.setStatus("1");
            userService.save(user);
            message = "用户添加成功";
        }

        //修改信息后删除对应用户的token信息
        if (removeToken) ProjectUtils.delCacheToken(user.getUsername(), null, false);
        return Return.T(true, message);
    }

    @PostMethod("/apiToken")
    public Return<?> setApiToken(@RequestParam String userId) {
        final User user = userService.getUserBySequence(userId);
        if (Verification.checkNull(user.getToken())) {
            user.setToken(UniqueSequenceUtils.getInstance().getUid());
            userService.save(user);
        }
        ProjectUtils.updateUserInterfaceTokenInfo(user);
        return Return.T(user.getToken());
    }

    @PostMethod("/offline")
    public Return<?> offline(@RequestParam(required = false) String username, @RequestParam(required = false) String role) {
        if (Verification.checkAllNull(username, role)) {
            ProjectUtils.delAllCacheToken();
        } else {
            ProjectUtils.delCacheToken(username, role, false);
        }
        return Return.SUCCESS;
    }

    @PostMethod(value = "/list/forbid")
    public Return<?> forbid(@RequestParam JSONObject request) {
        //修改状态信息
        String sequence = request.getString("sequence");
        String newStatus;
        String returnMessage;
        User user = userService.getUserBySequence(sequence);
        if (user.getStatus().equals("1")) {
            newStatus = "0";
            returnMessage = "禁止登录";
        } else {
            newStatus = "1";
            returnMessage = "状态已恢复";
        }
        userService.updateStatus(sequence, newStatus);
        user.setStatus(newStatus);
        ProjectUtils.updateUserInterfaceTokenInfo(user);
        return Return.T(true, returnMessage);
    }

    @GetMethod("/role/getData")
    public Return<?> getRole(@RequestParam("search") JSONObject search, @RequestParam int limit, @RequestParam int page) {
        String name = search.getString("name");
        String description = search.getString("description");
        String status = search.getString("status");
        //获取查询条件及页码返回的用户列表
        Page<Role> rolePage = roleService.search(page, limit, name, description, status);
        return Return.T(rolePage.getContent()).setTotalRecord(rolePage.getTotalElements());
    }

    @GetMethod("/role/selectMenu")
    public Return<?> roleSelectMenu() {
        //获取每个角色选中的菜单
        List<Role> roleList = roleService.getAll();
        List<RoleMenu> roleMenuList = roleMenuService.getAll();
        JSONObject roleSelect = new JSONObject();
        for (Role role : roleList) {
            List<List<Object>> roleSelectList = new ArrayList<>();
            for (RoleMenu roleMenu : roleMenuList) {
                if (role.getName().equals(roleMenu.getRole())) {
                    List<Object> select = Arrays.asList(roleMenu.getMenu(), roleMenu.getSubMenu(), Integer.parseInt(roleMenu.getPermission()));
                    roleSelectList.add(select);
                }
            }
            if (!roleSelectList.isEmpty()) {
                roleSelect.put(role.getName(), roleSelectList);
            }
        }

        return Return.T(roleSelect);
    }

    @GetMethod(value = "/role/permission",permission = BooleanType.TRUE)
    public Return<?> rolePermission(CacheToken cacheToken) {
        final List<RoleMenu> roleMenuList = roleMenuService.getAllByRole(cacheToken.getRole());
        final Map<String, List<Integer>> rolePermissionMap = new HashMap<>();
        for (RoleMenu roleMenu : roleMenuList) {
            final List<Integer> permissionList = Transformation.nvl(rolePermissionMap.get(roleMenu.getSubMenu()), new ArrayList<>());
            permissionList.add(Integer.parseInt(roleMenu.getPermission()));
            rolePermissionMap.put(roleMenu.getSubMenu(), permissionList);
        }

        final Map<String, Integer> rolePermission = new HashMap<>();
        for (String key : rolePermissionMap.keySet()) {
            rolePermission.put(Md5Utils.md5(key), PermissionUtils.mergePermission(rolePermissionMap.get(key)));
        }
        if (Verification.checkNull(rolePermission) && !GlobalEnv.SuperAdmin.equals(cacheToken.getRole())) {
            return RequestUtils.response401("无权限访问系统");
        }
        return Return.T(rolePermission);
    }

    @GetMethod("/role/menuData")
    public Return<?> roleMenuData() {
        //获取所有目录
        //根目录列表
        List<Menu> rootMenu = menuService.getRootMenu();
        //所有目录列表
        List<Menu> allMenu = menuService.getMenu();
        //最终展示的目录列表
        List<JSONObject> menuList = new ArrayList<>();
        //循环父节点,寻找子节点
        for (Menu root : rootMenu) {
            List<JSONObject> childMenu = new ArrayList<>();
            for (Menu all : allMenu) {
                if (root.getSequence().equals(all.getParentId())) {
                    childMenu.add(DictMap.of("label", all.getName(), "value", all.getUrl(), "status", all.getStatus()));
                }
            }
            if (childMenu.isEmpty()) {
                childMenu.add(DictMap.of("label", root.getName(), "value", root.getUrl(), "status", root.getStatus()));
            }
            menuList.add(DictMap.of("label", root.getName(), "value", root.getUrl(), "status", root.getStatus(), "children", childMenu));
        }

        List<JSONObject> permissionList = new ArrayList<>();

        for (PermissionUtils.Permission permission : PermissionUtils.getAllPermission()) {
            permissionList.add(DictMap.of("label", permission.getName(), "value", permission.getBinary()));
        }

        return Return.T(DictMap.of("menu", menuList, "permission", permissionList));
    }

    @PostMethod("/role/modify")
    public Return<?> modifyRole(@RequestParam JSONObject requestData, CacheToken cacheToken) {
        Role role = requestData.getObject("role", Role.class);
        if (Verification.checkNull(role.getSequence())) {
            Role findRole = roleService.getRoleByName(role.getName());
            if (findRole != null && !findRole.getSequence().equals(role.getSequence())) {
                return Return.T(false, String.format("角色:%s已存在", role.getName()));
            }

            role.setSequence(String.valueOf(System.currentTimeMillis()));
            role.setCreator(cacheToken.getUsername());
            role.setStatus("1");
            Timestamp nowTime = GetTime.dbTimestamp();
            role.setCrtTime(nowTime);
            role.setUpdateTime(nowTime);
        }
        try {
            roleService.updateRole(role);
            return Return.T(true, "提交成功");
        } catch (Exception e) {
            return Return.T(false, "提交失败");
        }
    }

    @PostMethod("/role/forbid")
    public Return<?> forbidRole(@RequestParam JSONObject request) {
        try {
            roleService.updateStatus(request.getString("sequence"));
            return Return.T(true, "操作成功");
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            return Return.T(false, "操作失败");
        }
    }

    @PostMethod("/role/changeVip")
    public Return<?> changeVip(@RequestParam JSONObject request) {
        try {
            roleService.updateVip(request.getString("sequence"));
            ProjectUtils.initInterfaceVip();
            return Return.T(true, "操作成功");
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            return Return.T(false, "操作失败");
        }
    }

    @PostMethod("/role/roleMenu")
    public Return<?> roleMenu(@RequestParam JSONObject request) {
        JSONObject roleMenu = request.getJSONObject("roleMenu");
        String role = roleMenu.getString("role");
        List<List<String>> selectData = JSONObject.parseObject(roleMenu.getString("selectData"), new TypeReference<>() {
        });

        if (selectData.isEmpty()) ProjectUtils.delCacheToken("", role, false);

        roleMenuService.deleteByRole(role);
        ArrayList<RoleMenu> roleMenus = new ArrayList<>();
        try {
            for (List<String> data : selectData) {
                RoleMenu menu = new RoleMenu();
                menu.setSequence(GetTime.nanoTime());
                menu.setMenu(data.get(0));
                menu.setSubMenu(data.get(1));
                menu.setPermission(data.get(2));
                menu.setCrtTime(GetTime.dbTimestamp());
                menu.setRole(role);
                roleMenus.add(menu);
            }
            roleMenuService.save(roleMenus);
            // ProjectUtils.delCacheToken(null, role, true);
            return Return.T(true, "保存成功");
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            return Return.T(false, "保存失败");
        }
    }

    @GetMethod("/info/getData")
    public Return<?> userInfo(CacheToken cacheToken) {
        User user = userService.getUser(cacheToken.getUsername());
        user.setPassword("");
        return Return.T(user);
    }

    @PostMethod("/info/modify")
    public Return<?> modifyInfo(@RequestParam JSONObject requestData) {
        User user = requestData.getObject("user", User.class);
        if (Verification.checkNull(user.getSequence())) return Return.T(false, "信息异常,无法修改");
        try {
            user.setUpdateTime(GetTime.dbTimestamp());
            if (!Verification.checkNull(user.getPassword())) user.setPassword(Pbkdf2Sha256.encode(user.getPassword()));
            userService.updateUser(user);
            return Return.T(true, "用户信息更新成功");
        } catch (DataIntegrityViolationException e) {
            String error = Transformation.getBaseException(e).getMessage();
            String message;
            if (error.toUpperCase().indexOf("INDEX_USER_MOB") > 0) {
                message = "手机号码已存在,请重新输入";
            } else if (error.toUpperCase().indexOf("INDEX_USER_MAIL") > 0) {
                message = "邮箱账户已存在,请重新输入";
            } else {
                message = "保存失败";
            }
            return Return.T(false, message);
        }
    }

    @PostMethod("/avatar")
    public Return<?> avatar(MultipartFile file, CacheToken cacheToken) throws IOException {
        final String sequence = cacheToken.getSequence();
        AssertUtils.state(Verification.checkNotNull(sequence), new InterfaceResponseException(ResponseCode.NoAuthorize));
        final String filename = sequence + ".jpg";
        final boolean hasKey = redisUtils.hasKey(ProjectUtils.getBucketName("avatar"));
        if (!hasKey) return Return.T(false, "桶不存在");
        final String fileId = minioUploadService.upload(file, "avatar", filename);
        final User user = userService.getUserBySequence(sequence).setAvatar(fileId);
        userService.save(user);
        return Return.T(Verification.checkNotNull(fileId), fileId);
    }
}
